package drds.plus.executor.utils;

import drds.plus.executor.cursor.cursor_metadata.ColumnNameAndTableNameAndColumnIndexAndNextHolder;
import drds.plus.executor.cursor.cursor_metadata.CursorMetaData;
import drds.plus.executor.cursor.cursor_metadata.CursorMetaDataImpl;
import drds.plus.executor.record_codec.record.FixedLengthRecord;
import drds.plus.executor.record_codec.record.Record;
import drds.plus.executor.row_values.ArrayRowValues;
import drds.plus.executor.row_values.ResultSetRowValues;
import drds.plus.executor.row_values.RowValues;
import drds.plus.executor.row_values.RowValuesWrapper;
import drds.plus.sql_process.abstract_syntax_tree.ObjectCreateFactory;
import drds.plus.sql_process.abstract_syntax_tree.configuration.ColumnMetaData;
import drds.plus.sql_process.abstract_syntax_tree.configuration.IndexMapping;
import drds.plus.sql_process.abstract_syntax_tree.execute_plan.query.Query;
import drds.plus.sql_process.abstract_syntax_tree.expression.NullValue;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.Item;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.column.Column;
import drds.plus.sql_process.abstract_syntax_tree.expression.item.function.Function;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderBy;
import drds.plus.sql_process.abstract_syntax_tree.expression.order_by.OrderByImpl;
import drds.plus.util.GeneralUtil;
import drds.tools.$;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Utils {


    public static void printCursorMetaData(CursorMetaData cursorMetaData, int inden, StringBuilder sb) {
        if (cursorMetaData != null) {
            sb.append(cursorMetaData.toStringWithInden(inden));
            sb.append("\n");
        }
    }


    public static void printOrderByList(List<OrderBy> orderByList, int inden, StringBuilder sb) {
        if (orderByList != null) {
            String tab = GeneralUtil.getTab(inden);
            sb.append(tab).append("order by : ");
            for (OrderBy orderBy : orderByList) {
                sb.append(orderBy.toStringWithInden(inden + 1)).append(" ");
            }
            sb.append("\n");
        }
    }

    public static Object get(Record record, ColumnMetaData columnMetaData) {
        return record.get(columnMetaData.getColumnName());
    }

    public static Object get(Record record, Column column) {
        return record.get(column.getColumnName());
    }


    public static Column getColumn(Object column) {
        return getIColumn(column);
    }

    public static ColumnMetaData getColumnMetaData(Object o) {
        Item item = (Item) o;
        return new ColumnMetaData(item.getTableName(), item.getColumnName(), item.getAlias(), item.getType(), true);
    }

    public static ColumnMetaData getColumnMetaData(Object o, String columnAlias) {
        Item item = (Item) o;
        return new ColumnMetaData(item.getTableName(), columnAlias, item.getAlias(), item.getType(), true);
    }

    public static ColumnMetaData getColumnMetaData(Object o, String tableName, String columnAlias) {
        Item item = (Item) o;
        return new ColumnMetaData(tableName, columnAlias, item.getAlias(), item.getType(), true);
    }

    public static ColumnMetaData getColumnMetaTable(Object o, String tableName) {
        Item item = (Item) o;
        return new ColumnMetaData(tableName, item.getColumnName(), item.getAlias(), item.getType(), true);
    }

    public static String getColumnName(int index, String columns) {
        String[] strs = columns.split(",");
        if (index >= strs.length) {
            return "error column columnName";
        }
        return strs[index];

    }

    public static String getColumnName(Object object) {
        if (object instanceof Column) {
            Column column = getIColumn(object);
            if (column.getAlias() != null) {
                return column.getAlias();
            } else {
                return column.getColumnName();
            }

        } else if (object instanceof Function) {
            Function function = ((Function) object);
            if (function.getAlias() != null) {
                return function.getAlias();
            } else {
                return function.getColumnName();
            }
        } else {
            return String.valueOf(object);
        }
    }


    public static Column getIColumn(Object column) {
        if (column instanceof Function) {
            Column column1 = ObjectCreateFactory.createColumn();
            column1.setTableName(((Function) column).getTableName());
            column1.setColumnName(((Function) column).getColumnName());
            column1.setAlias(((Function) column).getAlias());
            column1.setType(((Function) column).getType());
            return column1;
        } else if (!(column instanceof Column)) {
            throw new IllegalArgumentException("column :" + column + " is not a icolumn");
        }
        return (Column) column;
    }

    public static String getTableName(String indexName) {
        if (indexName == null) {
            return null;
        }
        int index = indexName.indexOf(".");
        if (index != -1) {
            return indexName.substring(0, index);
        } else {
            return indexName;
        }
    }

    public static List<OrderBy> getOrderByList(IndexMapping indexMapping) {
        if (indexMapping == null) {
            return new ArrayList<OrderBy>(0);
        }
        List<OrderBy> orderByList = new ArrayList<OrderBy>();
        for (ColumnMetaData columnMetaData : indexMapping.getKeyColumnMetaDataList()) {
            Column column = ObjectCreateFactory.createColumn();
            column.setTableName(columnMetaData.getTableName());
            column.setColumnName(columnMetaData.getColumnName());
            column.setAlias(columnMetaData.getAlias());
            column.setType(columnMetaData.getType());
            //
            OrderBy orderBy = new OrderByImpl();
            orderBy.setColumn(column);
            orderBy.setAsc(true);
            orderByList.add(orderBy);
        }
        return orderByList;
    }

    public static List<OrderBy> getOrderByListFromColumnMetaDataList(List<ColumnMetaData> columnMetaDataList) {
        if (!$.isNotNullAndHasElement(columnMetaDataList)) {
            return new ArrayList<OrderBy>(0);
        }
        List<OrderBy> orderByList = new ArrayList<OrderBy>();
        for (ColumnMetaData columnMetaData : columnMetaDataList) {
            Column column = ObjectCreateFactory.createColumn();
            column.setTableName(columnMetaData.getTableName());
            column.setColumnName(columnMetaData.getColumnName());
            column.setAlias(columnMetaData.getAlias());
            column.setType(columnMetaData.getType());
            //
            OrderBy orderBy = new OrderByImpl();
            orderBy.setColumn(column);
            orderBy.setAsc(true);
            orderByList.add(orderBy);
        }
        return orderByList;
    }


    public static RowValues fromRowDataToArrayRowData(RowValues rowData) {
        // 初衷是为了让mysql的代理换成实际数据
        if (rowData == null) {
            return null;
        }
        if (rowData instanceof ResultSetRowValues) {
            List<Object> valueList = rowData.getValueList();
            return new ArrayRowValues(rowData.getParentCursorMetaData(), valueList.toArray());
        } else if (rowData instanceof RowValuesWrapper) {
            return rowDataWrapperToArrayRowData((RowValuesWrapper) rowData);
        } else {
            return rowData;
        }
    }

    /**
     * 将Wrapper实际包含的rowset中的代理去掉
     */
    private static RowValues rowDataWrapperToArrayRowData(RowValuesWrapper rowDataWrapper) {
        if (rowDataWrapper == null) {
            return null;
        }

        RowValues parentRowData = rowDataWrapper.getRowData();
        if (parentRowData instanceof ResultSetRowValues) {
            List<Object> valueList = rowDataWrapper.getValueList();
            RowValues rowData = new ArrayRowValues(parentRowData.getParentCursorMetaData(), valueList.toArray());
            rowDataWrapper.setRowData(rowData);
        } else if (parentRowData instanceof RowValuesWrapper) {
            rowDataWrapperToArrayRowData((RowValuesWrapper) parentRowData);
        }
        return rowDataWrapper;
    }


    /**
     * 根据column meta取value
     */
    public static Object getValue(RowValues rowData, ColumnMetaData columnMetaData) {
        return getValue(rowData, columnMetaData.getTableName(), columnMetaData.getColumnName(), columnMetaData.getAlias());
    }

    public static Object getValue(RowValues rowData, Item item) {

        Integer index = null;
        if (rowData == null) {
            return null;
        }
        index = rowData.getParentCursorMetaData().getIndex(item.getTableName(), item.getColumnName(), item.getAlias());
        if (index == null) {
            return null;
        }
        Object object = rowData.getObject(index);
        return object;
    }

    public static Object getValue(RowValues rowData, String tableName, String columnName, String columnAlias) {
        Integer index = rowData.getParentCursorMetaData().getIndex(tableName, columnName, columnAlias);
        if (index == null) {
            return null;
        }
        Object object = rowData.getObject(index);
        return object;
    }

    public static List<Item> getIColumnsWithISelectable(ColumnMetaData[] columnMetaDatas) {
        List<Item> itemList = new ArrayList<Item>(columnMetaDatas.length);
        for (ColumnMetaData columnMetaData : columnMetaDatas) {
            Column column = getIColumnsFromColumnMetaData(columnMetaData);
            itemList.add(column);

        }
        return itemList;
    }

    public static Column getIColumnsFromColumnMetaData(ColumnMetaData columnMetaData) {
        Column column = ObjectCreateFactory.createColumn();
        column.setColumnName(columnMetaData.getColumnName());
        column.setTableName(columnMetaData.getTableName());
        column.setAlias(columnMetaData.getAlias());
        column.setType(columnMetaData.getType());
        if (column.getType() == null) {
            throw new RuntimeException("fuck!");
        }
        return column;
    }

    public static Column getIColumnsFromColumnMetaData(ColumnMetaData columnMetaData, String tableAlias) {
        Column column = ObjectCreateFactory.createColumn();
        column.setColumnName(columnMetaData.getColumnName());
        column.setTableName(tableAlias);
        column.setAlias(columnMetaData.getAlias());
        column.setType(columnMetaData.getType());
        //
        if (column.getType() == null) {
            throw new RuntimeException("fuck!");
        }
        return column;
    }


    public static List<ColumnMetaData> getColumnMetaDataList(List<Item> itemList) {
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>(itemList.size());
        for (Item item : itemList) {
            ColumnMetaData columnMetaData = new ColumnMetaData(item.getTableName(), item.getColumnName(), item.getAlias(), item.getType(), true);
            columnMetaDataList.add(columnMetaData);
        }
        return columnMetaDataList;
    }


    public static List<ColumnMetaData> convertOrderByListToColumnMetaDataListWithLogicTables(List<OrderBy> orderByList) {
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>(orderByList.size());
        for (OrderBy orderBy : orderByList) {
            ColumnMetaData columnMetaData = new ColumnMetaData(getTableName(orderBy.getTableName()), orderBy.getColumnName(), orderBy.getAlias(), orderBy.getDataType(), true);
            columnMetaDataList.add(columnMetaData);
        }
        return columnMetaDataList;
    }


    public static List<ColumnMetaData> convertListToColumnMetaDataList(List list) {
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>(list.size());
        for (Object object : list) {
            columnMetaDataList.add(getColumnMetaData(object));
        }
        return columnMetaDataList;
    }


    public static List<ColumnMetaData> convertItemListToColumnMetaDataList(List<Item> itemList) {
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>(itemList.size());
        for (Item item : itemList) {
            columnMetaDataList.add(getColumnMetaData(item));
        }
        return columnMetaDataList;
    }

    public static List<ColumnMetaData> convertItemListToColumnMetaDataList(String tableAlias, List<Item> itemList) {
        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>(itemList.size());
        for (Item item : itemList) {
            columnMetaDataList.add(new ColumnMetaData(tableAlias == null ? item.getTableName() : tableAlias, item.getAlias() == null ? item.getColumnName() : item.getAlias(), item.getAlias(), item.getType(), true));
        }
        return columnMetaDataList;
    }

    public static List<OrderBy> copyOrderByList(List<OrderBy> orderByList) {
        if (orderByList == null) {
            return null;
        }
        List<OrderBy> newOrderByList = new ArrayList(orderByList.size());
        for (OrderBy orderBy : orderByList) {
            newOrderByList.add(orderBy.copy());
        }
        return newOrderByList;
    }

    public static Record convertToRecord(RowValues rowData) {
        Iterator<ColumnNameAndTableNameAndColumnIndexAndNextHolder> columnNameAndIndexIterator = rowData.getParentCursorMetaData().columnNameAndTableNameAndColumnIndexAndNextHolderIterator();
        Record record = new FixedLengthRecord(rowData.getParentCursorMetaData().getColumnMetaDataList());
        while (columnNameAndIndexIterator.hasNext()) {
            ColumnNameAndTableNameAndColumnIndexAndNextHolder columnNameAndTableNameAndColumnIndexAndNextHolder = columnNameAndIndexIterator.next();
            record.put(columnNameAndTableNameAndColumnIndexAndNextHolder.getColumnName(), rowData.getObject(columnNameAndTableNameAndColumnIndexAndNextHolder.getIndex()));
        }
        return record;
    }

    public static CursorMetaData convertIndexMetaDataToCursorMetaData(Query query) {
        if (query.getSql() != null) {
            return null;
        }
        List<ColumnMetaData> columnMetaDataList = convertItemListToColumnMetaDataList(query.getAlias(), query.getItemList());
        CursorMetaData cursorMetaData = CursorMetaDataImpl.buildCursorMetaData(columnMetaDataList, columnMetaDataList.size());
        return cursorMetaData;
    }

    public static CursorMetaData convertIndexMetaDataToCursorMetaData(IndexMapping indexMapping) {

        List<ColumnMetaData> columnMetaDataList = new ArrayList<ColumnMetaData>();
        //
        List<ColumnMetaData> keyColumnMetaDataList = indexMapping.getKeyColumnMetaDataList();
        addNewColumnMetaData(keyColumnMetaDataList, columnMetaDataList);
        //
        List<ColumnMetaData> valueColumnMetaDataList = indexMapping.getValueColumnMetaDataList();
        addNewColumnMetaData(valueColumnMetaDataList, columnMetaDataList);
        //
        CursorMetaData cursorMetaData = CursorMetaDataImpl.buildCursorMetaData(columnMetaDataList, columnMetaDataList.size());
        return cursorMetaData;
    }

    private static void addNewColumnMetaData(List<ColumnMetaData> oldColumnMetaDataList, List<ColumnMetaData> newColumnMetaDataList) {
        for (ColumnMetaData oldColumnMetaData : oldColumnMetaDataList) {
            ColumnMetaData newColumnMetaData = new ColumnMetaData(getTableName(oldColumnMetaData.getTableName()), oldColumnMetaData.getColumnName(), oldColumnMetaData.getAlias(), oldColumnMetaData.getType(), oldColumnMetaData.isNullable());
            newColumnMetaDataList.add(newColumnMetaData);
        }
    }

    public static Object getObject(RowValues rowData, final CursorMetaData cursorMetaData, String tableName, String columnName, String columnAlias) {
        Integer index = cursorMetaData.getIndex(tableName, columnName, columnAlias);
        if (index == null) {
            throw new NullPointerException("not found :" + tableName + "." + columnName + " in CursorMetaData:" + cursorMetaData);
        }
        return rowData.getObject(index);
    }

    public static List<Item> copyItemList(List<Item> itemList) {
        if (itemList == null) {
            return null;
        }
        List<Item> newItemList = new ArrayList(itemList.size());
        for (Item selectable : itemList) {
            newItemList.add(selectable.copy());
        }
        return newItemList;
    }

    public static boolean isNull(Object o) {
        if (o == null) {
            return true;
        }
        return o instanceof NullValue;
    }

}
